An emulator is software that creates a virtual environment where a particular type of computing system can be operated. For example, the Java Virtual Machine creates an environment where programs written in the Java programming language can run. Other types of emulators virtualize an entire computing architecture. For example, there are emulators that emulate the Intel IA32 architecture (e.g., VMWare).
Typically, emulators work by interpreting some form of instructions relevant to the target environment, and a dynamic translator (JIT, or Just-In-Time Compiler) translates such instructions into a sequence of instructions in the native instruction set of the computing system on which the emulator is running. The instruction stream that the emulator interprets is a computer program. As with any computer program, the instruction stream contains many different types of instructions, including branches to other instructions. Branches are special in that they can change the sequential flow of execution of the program and direct it to a different instruction stream.
Indirect branches contain the target address in a register, while in direct branches the target is encoded in the instruction itself. In an indirect branch instruction, the target address cannot be ascertained until the instruction is decoded and its parameters are read. Furthermore, the target address of an indirect branch instruction can change between different executions of the instruction. As a consequence, predicting the path to be taken by such indirect branch instructions can be costly.
The use of JIT compilers adds more complexity to this problem. Whenever the emulator or the compiled code encounters an indirect branch instruction, it is necessary to search whether the target address has been compiled or not (if the target has been compiled, then the memory address of the compiled instruction needs to be retrieved as well). This process creates the extra cost of the search every time an indirect branch instruction is executed. Because an indirect branch potentially has a different target address every time it is executed, the target address cannot be directly embedded in the compiled code.